
# 从 blinker 库导入 Signal 类，用于实现信号与槽机制
from blinker import Signal
# 导入 uuid 模块，用于生成唯一标识符
import uuid
# 从 collections 模块导入 OrderedDict 类，用于创建有序字典
from collections import OrderedDict
# 导入 copy 模块中的 copy 函数，用于浅拷贝对象
from copy import copy

# 从 inspect 模块导入 getfullargspec 函数，用于获取函数的参数信息
from inspect import getfullargspec
# 从 types 模块导入 MethodType 类，用于动态绑定方法
from types import MethodType
# 导入 traceback 模块，用于打印异常的堆栈信息
import traceback
# 从 PyFlow 模块导入根据引脚类型获取默认值的函数
from src import getPinDefaultValueByType
# 从 PyFlow 核心的通用模块导入所有内容
from src.Core.Common import *
# 从 PyFlow 核心的接口模块导入 INode 接口
from src.Core.Interfaces import INode
# 从 PyFlow 模块导入创建原始引脚的函数
from src import CreateRawPin

# 导入 datetime 模块中的 datetime 类，用于记录时间
from datetime import datetime


class NodePinsSuggestionsHelper(object):
    """
    描述节点输入和输出引脚的类型和结构。
    供节点框使用，用于推荐合适的节点。
    """

    def __init__(self):
        """
        初始化 NodePinsSuggestionsHelper 类的实例。
        """
        super(NodePinsSuggestionsHelper, self).__init__()
        # 定义模板，包含输入和输出的类型和结构信息
        self.template = {
            "types": {"inputs": [], "outputs": []},
            "structs": {"inputs": [], "outputs": []},
        }
        # 存储输入引脚的数据类型集合
        self.inputTypes = set()
        # 存储输出引脚的数据类型集合
        self.outputTypes = set()
        # 存储输入引脚的结构集合
        self.inputStructs = set()
        # 存储输出引脚的结构集合
        self.outputStructs = set()

    def addInputDataType(self, dataType):
        """
        向输入引脚的数据类型集合中添加一个数据类型。

        :param dataType: 要添加的数据类型
        """
        self.inputTypes.add(dataType)

    def addOutputDataType(self, dataType):
        """
        向输出引脚的数据类型集合中添加一个数据类型。

        :param dataType: 要添加的数据类型
        """
        self.outputTypes.add(dataType)

    def addInputStruct(self, struct):
        """
        向输入引脚的结构集合中添加一个结构。

        :param struct: 要添加的结构
        """
        self.inputStructs.add(struct)

    def addOutputStruct(self, struct):
        """
        向输出引脚的结构集合中添加一个结构。

        :param struct: 要添加的结构
        """
        self.outputStructs.add(struct)


class NodeBase(INode):
    # 节点所属的包名，初始为空
    _packageName = ""

    def __init__(self, name, uid=None):
        """
        初始化 NodeBase 类的实例。

        :param name: 节点的名称
        :param uid: 节点的唯一标识符，默认为 None，若为 None 则自动生成
        """
        super(NodeBase, self).__init__()
        # 标记是否启用缓存
        self.bCacheEnabled = True
        # 缓存的最大容量
        self.cacheMaxSize = 1000
        # 缓存字典，用于存储缓存数据
        self.cache = {}

        # 定义节点被销毁时发出的信号
        self.killed = Signal()
        # 定义节点进行定时更新时发出的信号，携带时间间隔参数
        self.tick = Signal(float)
        # 定义节点标记为脏数据时发出的信号
        self.setDirty = Signal()
        # 定义节点开始计算时发出的信号
        self.computing = Signal()
        # 定义节点计算完成时发出的信号
        self.computed = Signal()
        # 定义节点发生错误时发出的信号，携带错误信息
        self.errorOccurred = Signal(object)
        # 定义节点错误清除时发出的信号
        self.errorCleared = Signal()

        # 标记节点是否为脏数据
        self.dirty = True
        # 节点的唯一标识符，如果未提供则自动生成
        self._uid = uuid.uuid4() if uid is None else uid
        # 节点所属的图对象的弱引用
        self.graph = None
        # 节点的名称
        self.name = name
        # 记录引脚创建顺序的有序字典
        self.pinsCreationOrder = OrderedDict()
        # 存储节点所有引脚的集合
        self._pins = set()
        # 节点在画布上的 x 坐标
        self.x = 0.0
        # 节点在画布上的 y 坐标
        self.y = 0.0
        # 标记节点是否可调用
        self.bCallable = False
        # 节点的包装器对象
        self._wrapper = None
        # 引脚的约束条件字典
        self._constraints = {}
        # 引脚结构的约束条件字典
        self._structConstraints = {}
        # 节点所属的库名
        self.lib = None
        # 标记节点是否为复合节点
        self.isCompoundNode = False
        # 记录节点的最后一个错误信息
        self._lastError = None
        # 存储包装器的 JSON 数据
        self.__wrapperJsonData = None
        # 存储节点的元数据
        self._nodeMetaData = None
        # 节点头部的颜色
        self.headerColor = None
        # 标记节点是否已弃用
        self._deprecated = False
        # 节点弃用时的提示信息
        self._deprecationMessage = "This node is deprecated"
        # 标记节点是否为实验性节点
        self._experimental = False
        # 记录节点的计算时间
        self._computingTime = None

        self.input_pins = {}
        self.output_pins = {}

    def setDeprecated(self, message):
        """
        将节点标记为已弃用，并设置弃用提示信息。

        :param message: 额外的弃用提示信息
        """
        self._deprecated = True
        self._deprecationMessage = "This node will be removed in later releases! {}".format(
            message
        )

    def isDeprecated(self):
        """
        判断节点是否已弃用。

        :return: 如果节点已弃用返回 True，否则返回 False
        """
        return self._deprecated

    def isExperimental(self):
        """
        判断节点是否为实验性节点。

        :return: 如果节点是实验性节点返回 True，否则返回 False
        """
        return self._experimental

    def setExperimental(self):
        """
        将节点标记为实验性节点。
        """
        self._experimental = True

    def deprecationMessage(self):
        """
        获取节点的弃用提示信息。

        :return: 节点的弃用提示信息
        """
        return self._deprecationMessage

    def getMetaData(self):
        """
        返回节点的元数据字典。

        此方法仅对基于函数的节点返回字典，对于基于类的节点返回 None。

        .. seealso:: :mod:`~PyFlow.Core.FunctionLibrary`

        :return: 节点的元数据字典或 None
        """
        return self._nodeMetaData

    @property
    def wrapperJsonData(self):
        """
        获取包装器的 JSON 数据，并清空存储。

        :return: 包装器的 JSON 数据，如果发生异常则返回 None
        """
        try:
            # 复制包装器的 JSON 数据
            dt = self.__wrapperJsonData.copy()
            # 清空存储的包装器 JSON 数据
            self.__wrapperJsonData.clear()
            self.__wrapperJsonData = None
            return dt
        except Exception as e:
            return None

    def isValid(self):
        """
        判断节点是否有效，即是否没有错误信息。

        :return: 如果节点没有错误信息返回 True，否则返回 False
        """
        return self._lastError is None

    def getLastErrorMessage(self):
        """
        获取节点的最后一个错误信息。

        :return: 节点的最后一个错误信息
        """
        return self._lastError

    def clearError(self):
        """
        清除节点的错误信息，并发出错误清除信号。
        """
        self._lastError = None
        self.errorCleared.send()

    def setError(self, err):
        """
        设置节点的错误信息，并发出错误发生信号。

        :param err: 错误信息
        """
        self._lastError = str(err)
        self.errorOccurred.send(self._lastError)

    def checkForErrors(self):
        """
        检查节点的引脚是否有错误，并更新节点的错误信息。
        如果有包装器，还会更新包装器。
        """
        # 存储有错误的引脚及其错误信息的字典
        failedPins = {}
        for pin in self._pins:
            if pin._lastError is not None:
                failedPins[pin.name] = pin._lastError
        if len(failedPins):
            self._lastError = "Error on Pins:%s" % str(failedPins)
        else:
            self.clearError()
        # 获取节点的包装器对象
        wrapper = self.getWrapper()
        if wrapper:
            wrapper.update()

    @property
    def packageName(self):
        """
        获取节点所属的包名。

        :return: 节点所属的包名
        """
        return self._packageName

    @property
    def constraints(self):
        """
        获取引脚的约束条件字典。

        :return: 引脚的约束条件字典
        """
        return self._constraints

    @property
    def structConstraints(self):
        """
        获取引脚结构的约束条件字典。

        :return: 引脚结构的约束条件字典
        """
        return self._structConstraints

    def getOrderedPins(self):
        """
        获取按创建顺序排列的引脚列表。

        :return: 按创建顺序排列的引脚列表
        """
        return self.pinsCreationOrder.values()

    def getter(self, pinName):
        """
        根据引脚名称获取引脚对象。

        :param pinName: 引脚名称
        :return: 引脚对象，如果未找到则抛出异常
        """
        pin = self.getPinByName(pinName)
        if not pin:
            raise Exception()
        else:
            return pin

    def __getitem__(self, pinName):
        """
        通过索引操作符获取引脚对象。

        :param pinName: 引脚名称
        :return: 引脚对象，如果未找到则抛出异常
        """
        try:
            return self.getter(pinName)
        except Exception as x:
            if "<str>" in str(x):
                try:
                    return self.getter(str(pinName))
                except:
                    raise Exception("Could not find pin with name:{0}".format(pinName))
            else:
                raise Exception(
                    "Could not find signature for __getitem__:{0}".format(type(pinName))
                )

    @property
    def pins(self):
        """
        获取节点的所有引脚集合。

        :return: 节点的所有引脚集合
        """
        return self._pins

    @property
    def inputs(self):
        """
        返回节点的所有输入引脚。
        每次调用该属性时都会生成一个新的字典，因此如果可能请缓存结果。

        :return: 包含所有输入引脚的有序字典，键为引脚的唯一标识符，值为引脚对象
        """
        result = OrderedDict()
        for pin in self.pins:
            if pin.direction == PinDirection.Input:
                result[pin.uid] = pin
        return result

    @property
    def orderedInputs(self):
        """
        返回按引脚索引排序的输入引脚字典。

        :return: 按引脚索引排序的输入引脚字典，键为引脚索引，值为引脚对象
        """
        result = {}
        sortedInputs = sorted(self.inputs.values(), key=lambda x: x.pinIndex)
        for inp in sortedInputs:
            result[inp.pinIndex] = inp
        return result

    @property
    def namePinInputsMap(self):
        """
        返回节点的所有输入引脚，键为引脚名称。
        每次调用该属性时都会生成一个新的字典，因此如果可能请缓存结果。

        :return: 包含所有输入引脚的有序字典，键为引脚名称，值为引脚对象
        """
        result = OrderedDict()
        for pin in self.pins:
            if pin.direction == PinDirection.Input:
                result[pin.name] = pin
        return result

    @property
    def outputs(self):
        """
        返回节点的所有输出引脚。
        每次调用该属性时都会生成一个新的字典，因此如果可能请缓存结果。

        :return: 包含所有输出引脚的有序字典，键为引脚的唯一标识符，值为引脚对象
        """
        result = OrderedDict()
        for pin in self.pins:
            if pin.direction == PinDirection.Output:
                result[pin.uid] = pin
        return result

    @property
    def orderedOutputs(self):
        """
        返回按引脚索引排序的输出引脚字典。

        :return: 按引脚索引排序的输出引脚字典，键为引脚索引，值为引脚对象
        """
        result = {}
        sortedOutputs = sorted(self.outputs.values(), key=lambda x: x.pinIndex)
        for out in sortedOutputs:
            result[out.pinIndex] = out
        return result

    @property
    def namePinOutputsMap(self):
        """
        返回节点的所有输出引脚，键为引脚名称。
        每次调用该属性时都会生成一个新的字典，因此如果可能请缓存结果。

        :return: 包含所有输出引脚的有序字典，键为引脚名称，值为引脚对象
        """
        result = OrderedDict()
        for pin in self.pins:
            if pin.direction == PinDirection.Output:
                result[pin.name] = pin
        return result

    # IItemBase 接口

    def setWrapper(self, wrapper):
        """
        设置节点的包装器对象，仅在包装器为空时设置。

        :param wrapper: 包装器对象
        """
        if self._wrapper is None:
            self._wrapper = wrapper

    def getWrapper(self):
        """
        获取节点的包装器对象。

        :return: 节点的包装器对象，如果没有则返回 None
        """
        return self._wrapper

    def location(self):
        """
        返回节点所属图在图树中的路径。

        :return: 节点所属图在图树中的路径列表
        """
        return self.graph().location()

    def path(self):
        """
        返回节点在图树中的完整路径。

        :return: 节点在图树中的完整路径字符串
        """
        location = "/".join(self.location())
        return "{}/{}".format(location, self.getName())

    @property
    def uid(self):
        """
        获取节点的唯一标识符。

        :return: 节点的唯一标识符
        """
        return self._uid

    @uid.setter
    def uid(self, value):
        """
        设置节点的唯一标识符，并更新图中节点的存储。

        :param value: 新的唯一标识符
        """
        if self.graph is not None:
            self.graph().getNodes()[value] = self.graph().getNodes().pop(self._uid)
        self._uid = value

    @staticmethod
    def jsonTemplate():
        """
        返回节点序列化的 JSON 模板。

        :return: 节点序列化的 JSON 模板字典
        """
        template = {
            "package": None,
            "lib": None,
            "type": None,
            "owningGraphName": None,
            "name": None,
            "uuid": None,
            "inputs": [],
            "outputs": [],
            "meta": {"var": {}},
            "wrapper": {},
        }
        return template

    def serialize(self):
        """
        将节点序列化为 JSON 格式的数据。

        :return: 节点序列化后的 JSON 格式数据字典
        """
        template = NodeBase.jsonTemplate()

        # 将节点的唯一标识符转换为字符串
        uidString = str(self.uid)
        # 获取节点的名称
        nodeName = self.name

        template["package"] = self.packageName
        template["lib"] = self.lib
        template["type"] = self.__class__.__name__
        template["name"] = nodeName
        template["owningGraphName"] = self.graph().name
        template["uuid"] = uidString
        template["inputs"] = [i.serialize() for i in self.inputs.values()]
        template["outputs"] = [o.serialize() for o in self.outputs.values()]
        template["meta"]["label"] = self.name
        template["x"] = self.x
        template["y"] = self.y

        # 如果存在包装器，获取包装器的序列化数据
        wrapper = self.getWrapper()
        if wrapper:
            template["wrapper"] = wrapper.serializationHook()
        return template

    def isUnderActiveGraph(self):
        """
        判断节点是否在当前活动图中。

        :return: 如果节点在当前活动图中返回 True，否则返回 False
        """
        return self.graph() == self.graph().graphManager.activeGraph()

    def kill(self, *args, **kwargs):
        """
        销毁节点，包括发出销毁信号、销毁引脚、从图中移除节点，并重建路径注册表。
        """
        from src.Core.PathsRegistry import PathsRegistry

        if self.uid not in self.graph().getNodes():
            return

        # 发出节点销毁信号
        self.killed.send()

        # 销毁所有输入引脚
        for pin in self.inputs.values():
            pin.kill()
        # 销毁所有输出引脚
        for pin in self.outputs.values():
            pin.kill()
        # 从图的节点存储中移除当前节点
        self.graph().getNodes().pop(self.uid)

        # 重建路径注册表
        PathsRegistry().rebuild()

    def Tick(self, delta):
        """
        节点的定时更新方法，发出定时更新信号。

        :param delta: 时间间隔
        """
        self.tick.send(delta)

    @staticmethod
    def category():
        """
        返回节点的类别，默认为 "Default"。

        :return: 节点的类别
        """
        return "Default"

    @staticmethod
    def keywords():
        """
        返回节点的关键字列表，默认为空列表。

        :return: 节点的关键字列表
        """
        return []

    @staticmethod
    def pinTypeHints():
        """
        返回节点引脚类型的建议帮助对象。

        :return: 节点引脚类型的建议帮助对象
        """
        return NodePinsSuggestionsHelper()

    @staticmethod
    def description():
        """
        返回节点的描述信息，默认为 "Default node description"。

        :return: 节点的描述信息
        """
        return "Default node description"

    def getName(self):
        """
        获取节点的名称。

        :return: 节点的名称
        """
        return self.name

    def setName(self, name):
        """
        设置节点的名称。

        :param name: 新的节点名称
        """
        self.name = str(name)

    def isDirty(self):
        """
        判断节点是否为脏数据，即输入或输出引脚是否有脏数据。

        :return: 如果输入或输出引脚有脏数据返回 True，否则返回 False
        """
        inpDirty = any([pin.dirty for pin in self.inputs.values() if pin.IsValuePin()])
        outDirty = any([pin.dirty for pin in self.outputs.values() if pin.IsValuePin()])
        return inpDirty or outDirty

    def afterCompute(self):
        """
        节点计算完成后调用的方法，将所有输入和输出引脚标记为干净数据。
        """
        for pin in self.inputs.values():
            pin.setClean()
        for pin in self.outputs.values():
            pin.setClean()

    def processNode(self, *args, **kwargs):
        """
        处理节点的计算过程，记录计算时间，处理缓存和错误信息。

        :param args: 可变参数
        :param kwargs: 关键字参数
        """
        # 记录计算开始时间
        start = datetime.now()
        # if not self.isValid():
        #    return
        # 发出节点开始计算的信号
        self.computing.send()
        if self.bCacheEnabled:
            if self.isDirty():
                try:
                    # 调用节点的计算方法
                    self.compute()
                    # 清除节点的错误信息
                    self.clearError()
                    # 检查节点的引脚是否有错误
                    self.checkForErrors()
                    # 节点计算完成后处理
                    self.afterCompute()
                except Exception as e:
                    # 设置节点的错误信息
                    self.setError(traceback.format_exc())
        else:
            try:
                # 调用节点的计算方法
                self.compute()
                # 清除节点的错误信息
                self.clearError()
                # 检查节点的引脚是否有错误
                self.checkForErrors()
            except Exception as e:
                # 设置节点的错误信息
                self.setError(traceback.format_exc())
        # 计算计算时间
        delta = datetime.now() - start
        self._computingTime = delta
        # 发出节点计算完成的信号
        self.computed.send()

    # INode 接口

    def compute(self, *args, **kwargs):
        """
        节点的核心计算方法，主要逻辑在此处实现。

        基本步骤如下：
        1. 从输入引脚获取数据
        2. 进行计算操作
        3. 将结果设置到输出引脚
        4. 根据需要调用执行引脚

        以下是 charge 节点的 compute 方法示例：

        .. code-block:: python
            :linenos:

            def compute(self, *args, **kwargs):
                step = abs(self.step.getData())
                if (self._currentAmount + step) < abs(self.amount.getData()):
                    self._currentAmount += step
                    return
                self.completed.call(*args, **kwargs)
                self._currentAmount = 0.0

        .. note:: 更多示例请参考 :mod:`PyFlow.Packages.PyFlowBase.Nodes` 模块的源代码

        :param args: 可变参数
        :param kwargs: 关键字参数
        """
        pass

    # INode 接口结束

    def isCallable(self):
        """
        判断节点是否可调用，即节点是否包含执行引脚。

        :return: 如果节点包含执行引脚返回 True，否则返回 False
        """
        for p in list(self.inputs.values()) + list(self.outputs.values()):
            if p.isExec():
                return True
        return False

    def setPosition(self, x, y):
        """
        设置节点在画布上的坐标，用于正确恢复 GUI 包装器类。

        :param x: X 坐标
        :type x: float
        :param y: Y 坐标
        :type y: float
        """
        self.x = x
        self.y = y

    def autoAffectPins(self):
        """
        自动设置引脚之间的影响关系：所有值输入引脚影响所有值输出引脚，所有执行输入引脚影响所有执行输出引脚。
        """
        for i in self.inputs.values():
            for o in self.outputs.values():
                assert i is not o
                if not i.IsValuePin() and o.IsValuePin():
                    continue
                if i.IsValuePin() and not o.IsValuePin():
                    continue
                # 设置引脚之间的影响关系
                pinAffects(i, o)

    def createInputPin(
        self,
        pinName,
        dataType,
        defaultValue=None,
        callback=None,
        structure=StructureType.Single,
        constraint=None,
        structConstraint=None,
        supportedPinDataTypes=None,
        group="",
    ):
        """
        创建一个输入引脚。

        :param pinName: 引脚名称
        :type pinName: str
        :param dataType: 引脚的数据类型
        :type dataType: str
        :param defaultValue: 引脚的默认值
        :type defaultValue: object
        :param callback: 引脚的回调函数，用于执行引脚
        :type callback: function
        :param structure: 引脚的结构类型
        :type structure: :class:`~PyFlow.Core.Common.StructureType.Single`
        :param constraint: 引脚的约束条件，应为可哈希类型，通常使用字符串
        :type constraint: object
        :param structConstraint: 引脚结构的约束条件，也应为可哈希类型
        :type structConstraint: object
        :param supportedPinDataTypes: 允许连接的引脚数据类型列表，供 AnyPin 使用
        :type supportedPinDataTypes: list(str)
        :param group: 引脚所属的组，仅由 UI 包装器使用
        :type group: str
        :return: 创建的输入引脚对象
        """
        # 为引脚生成唯一的名称
        pinName = self.getUniqPinName(pinName)
        # 创建原始输入引脚
        p = CreateRawPin(pinName, self, dataType, PinDirection.Input)
        # 设置引脚的结构类型
        p.structureType = structure
        # 设置引脚所属的组
        p.group = group

        if structure == StructureType.Array:
            # 初始化引脚为数组类型
            p.initAsArray(True)
        elif structure == StructureType.Dict:
            # 初始化引脚为字典类型
            p.initAsDict(True)
        elif structure == StructureType.Multi:
            # 启用引脚的数组支持选项
            p.enableOptions(PinOptions.ArraySupported)

        if callback:
            # 连接引脚的执行信号到回调函数
            p.onExecute.connect(callback, weak=False)

        if defaultValue is not None or dataType == "AnyPin":
            # 设置引脚的默认值和数据
            p.setDefaultValue(defaultValue)
            p.setData(defaultValue)
            if dataType == "AnyPin":
                # 根据数据设置引脚的类型
                p.setTypeFromData(defaultValue)
        else:
            # 设置引脚的默认值为对应类型的默认值
            p.setDefaultValue(getPinDefaultValueByType(dataType))

        if dataType == "AnyPin" and supportedPinDataTypes:
            def supportedDataTypes():
                """
                返回允许连接的引脚数据类型列表。

                :return: 允许连接的引脚数据类型列表
                """
                return supportedPinDataTypes

            # 设置引脚允许连接的数据类型
            p._supportedDataTypes = p._defaultSupportedDataTypes = tuple(
                supportedPinDataTypes
            )
            p.supportedDataTypes = supportedDataTypes
        if constraint is not None:
            # 更新引脚的约束条件
            p.updateConstraint(constraint)
        if structConstraint is not None:
            # 更新引脚结构的约束条件
            p.updateStructConstraint(structConstraint)
        # 连接引脚的数据设置和标记为脏数据信号到节点的 setDirty 信号
        p.dataBeenSet.connect(self.setDirty.send)
        p.markedAsDirty.connect(self.setDirty.send)
        return p

    def createOutputPin(
        self,
        pinName,
        dataType,
        defaultValue=None,
        structure=StructureType.Single,
        constraint=None,
        structConstraint=None,
        supportedPinDataTypes=None,
        group="",
    ):
        """
        创建一个输出引脚。

        :param pinName: 引脚名称
        :type pinName: str
        :param dataType: 引脚的数据类型
        :type dataType: str
        :param defaultValue: 引脚的默认值
        :type defaultValue: object
        :param structure: 引脚的结构类型
        :type structure: :class:`~PyFlow.Core.Common.StructureType.Single`
        :param constraint: 引脚的约束条件，应为可哈希类型，通常使用字符串
        :type constraint: object
        :param structConstraint: 引脚结构的约束条件，也应为可哈希类型
        :type structConstraint: object
        :param supportedPinDataTypes: 允许连接的引脚数据类型列表，供 AnyPin 使用
        :type supportedPinDataTypes: list(str)
        :param group: 引脚所属的组，仅由 UI 包装器使用
        :type group: str
        :return: 创建的输出引脚对象
        """
        # 为引脚生成唯一的名称
        pinName = self.getUniqPinName(pinName)
        # 创建原始输出引脚
        p = CreateRawPin(pinName, self, dataType, PinDirection.Output)
        # 设置引脚的结构类型
        p.structureType = structure
        # 设置引脚所属的组
        p.group = group

        if structure == StructureType.Array:
            # 初始化引脚为数组类型
            p.initAsArray(True)
        elif structure == StructureType.Dict:
            # 初始化引脚为字典类型
            p.initAsDict(True)
        elif structure == StructureType.Multi:
            # 启用引脚的数组支持选项
            p.enableOptions(PinOptions.ArraySupported)

        if defaultValue is not None or dataType == "AnyPin":
            # 设置引脚的默认值和数据
            p.setDefaultValue(defaultValue)
            p.setData(defaultValue)
            if dataType == "AnyPin":
                # 根据数据设置引脚的类型
                p.setTypeFromData(defaultValue)
        else:
            # 设置引脚的默认值为对应类型的默认值
            p.setDefaultValue(getPinDefaultValueByType(dataType))

        if dataType == "AnyPin" and supportedPinDataTypes:
            def supportedDataTypes():
                """
                返回允许连接的引脚数据类型列表。

                :return: 允许连接的引脚数据类型列表
                """
                return supportedPinDataTypes

            # 设置引脚允许连接的数据类型
            p.supportedDataTypes = supportedDataTypes
        if constraint is not None:
            # 更新引脚的约束条件
            p.updateConstraint(constraint)
        if structConstraint is not None:
            # 更新引脚结构的约束条件
            p.updateStructConstraint(structConstraint)
        return p

    def removePin(self, pinName):
        """
        根据引脚名称移除输入或输出引脚。

        :param pinName: 要移除的引脚名称
        """
        # 先尝试移除输入引脚
        for pin_id, pin in list(self.input_pins.items()):
            if pin["name"] == pinName:
                del self.input_pins[pin_id]
                return
        # 若输入引脚中未找到，尝试移除输出引脚
        for pin_id, pin in list(self.output_pins.items()):
            if pin["name"] == pinName:
                del self.output_pins[pin_id]
                return

    def setData(self, pinName, data, pinSelectionGroup=PinSelectionGroup.BothSides):
        """
        根据引脚名称设置引脚的数据。

        :param pinName: 目标引脚名称
        :type pinName: str
        :param data: 要设置的引脚数据
        :type data: object
        :param pinSelectionGroup: 搜索的引脚侧别
        :type pinSelectionGroup: :class:`~PyFlow.Core.Common.PinSelectionGroup`
        """
        # 根据引脚名称和搜索侧别获取引脚对象
        p = self.getPinSG(str(pinName), pinSelectionGroup)
        assert p is not None, "Failed to find pin by name: {}".format(pinName)
        # 设置引脚的数据
        p.setData(data)

    def getData(self, pinName, pinSelectionGroup=PinSelectionGroup.BothSides):
        """
        根据引脚名称获取引脚的数据。

        :param pinName: 目标引脚名称
        :type pinName: str
        :param pinSelectionGroup: 搜索的引脚侧别
        :type pinSelectionGroup: :class:`~PyFlow.Core.Common.PinSelectionGroup`
        :return: 引脚的数据
        """
        # 根据引脚名称和搜索侧别获取引脚对象
        p = self.getPinSG(str(pinName), pinSelectionGroup)
        assert p is not None, "Failed to find pin by name: {}".format(pinName)
        # 获取引脚的数据
        return p.getData()

    def getUniqPinName(self, name):
        """
        返回引脚的唯一名称。

        :param name: 目标引脚名称
        :type name: str
        :return: 唯一的引脚名称
        """
        # 获取所有输入和输出引脚的名称列表
        pinNames = [
            i.name
            for i in list(list(self.inputs.values()))
            + list(list(self.outputs.values()))
        ]
        # 从现有名称列表中生成唯一名称
        return getUniqNameFromList(pinNames, name)

    def __repr__(self):
        """
        返回节点的字符串表示形式，用于调试和打印输出。

        :return: 节点的字符串表示形式
        """
        graphName = self.graph().name if self.graph is not None else str(None)
        return "<class[{0}]; name[{1}]; graph[{2}]>".format(
            self.__class__.__name__, self.getName(), graphName
        )

    def call(self, name, *args, **kwargs):
        """
        根据引脚名称调用执行引脚。

        :param name: 目标引脚名称
        :type name: str
        :param args: 可变参数
        :param kwargs: 关键字参数
        """
        # 获取输出引脚的名称到引脚对象的映射字典
        namePinOutputsMap = self.namePinOutputsMap
        # 获取输入引脚的名称到引脚对象的映射字典
        namePinInputsMap = self.namePinInputsMap
        if name in namePinOutputsMap:
            p = namePinOutputsMap[name]
            if p.isExec():
                # 调用执行引脚
                p.call(*args, **kwargs)
        if name in namePinInputsMap:
            p = namePinInputsMap[name]
            if p.isExec():
                # 调用执行引脚
                p.call(*args, **kwargs)

    def getPinSG(self, name, pinsSelectionGroup=PinSelectionGroup.BothSides):
        """
        尝试根据引脚名称和选择组查找引脚对象。

        :param name: 要搜索的引脚名称
        :type name: str
        :param pinsSelectionGroup: 搜索的引脚侧别
        :type pinsSelectionGroup: :class:`~PyFlow.Core.Common.PinSelectionGroup`
        :return: 找到的引脚对象，如果未找到则返回 None
        """
        # 获取所有输入引脚
        inputs = self.inputs
        # 获取所有输出引脚
        outputs = self.outputs
        if pinsSelectionGroup == PinSelectionGroup.BothSides:
            for p in list(inputs.values()) + list(outputs.values()):
                if p.name == name:
                    return p
        elif pinsSelectionGroup == PinSelectionGroup.Inputs:
            for p in list(inputs.values()):
                if p.name == name:
                    return p
        else:
            for p in list(outputs.values()):
                if p.name == name:
                    return p

    def getPinByName(self, name):
        """
        尝试根据引脚名称查找引脚对象。

        :param name: 引脚名称
        :type name: str
        :return: 找到的引脚对象，如果未找到则返回 None
        """
        # 获取所有输入引脚
        inputs = self.inputs
        # 获取所有输出引脚
        outputs = self.outputs
        for p in list(inputs.values()) + list(outputs.values()):
            if p.name == name:
                return p

    def postCreate(self, jsonTemplate=None):
        """
        节点添加到图后调用的方法，用于恢复节点的序列化数据。

        :param jsonTemplate: 生成节点的序列化数据
        :type jsonTemplate: dict or None
        """
        if jsonTemplate is not None:
            # 设置节点的唯一标识符
            self.uid = uuid.UUID(jsonTemplate["uuid"])
            # 设置节点的名称
            self.setName(jsonTemplate["name"])
            # 设置节点在画布上的 x 坐标
            self.x = jsonTemplate["x"]
            # 设置节点在画布上的 y 坐标
            self.y = jsonTemplate["y"]

            # 设置引脚数据
            # 按引脚索引排序输入引脚的序列化数据
            sortedInputs = sorted(
                jsonTemplate["inputs"], key=lambda pinDict: pinDict["pinIndex"]
            )
            for inpJson in sortedInputs:
                # 判断输入引脚是否启用动态选项
                dynamicEnabled = PinOptions.Dynamic.value in inpJson["options"]
                if dynamicEnabled or inpJson["name"] not in self.namePinInputsMap:
                    # 在派生类中创建自定义动态创建的引脚
                    continue

                # 根据输入引脚名称和选择组获取引脚对象
                pin = self.getPinSG(str(inpJson["name"]), PinSelectionGroup.Inputs)
                # 反序列化输入引脚的数据
                pin.deserialize(inpJson)

            # 按引脚索引排序输出引脚的序列化数据
            sortedOutputs = sorted(
                jsonTemplate["outputs"], key=lambda pinDict: pinDict["pinIndex"]
            )
            for outJson in sortedOutputs:
                # 判断输出引脚是否启用动态选项
                dynamicEnabled = PinOptions.Dynamic.value in outJson["options"]
                if dynamicEnabled or outJson["name"] not in self.namePinOutputsMap:
                    # 在派生类中创建自定义动态创建的引脚
                    continue

                # 根据输出引脚名称和选择组获取引脚对象
                pin = self.getPinSG(str(outJson["name"]), PinSelectionGroup.Outputs)
                # 反序列化输出引脚的数据
                pin.deserialize(outJson)

            # 存储包装器的数据
            if "wrapper" in jsonTemplate:
                self.__wrapperJsonData = jsonTemplate["wrapper"]

        if self.isCallable():
            # 标记节点为可调用
            self.bCallable = True

        # 没有输入引脚的节点缓存无意义
        # if len(self.inputs) == 0:
        #    self.bCacheEnabled = False

        # 自动设置引脚之间的影响关系
        self.autoAffectPins()
        # 检查节点的引脚是否有错误
        self.checkForErrors()

    @staticmethod
    def initializeFromFunction(foo):
        """
        从带注释的函数构造节点。

        .. seealso :: :mod:`PyFlow.Core.FunctionLibrary`

        :param foo: 带注释的函数
        :type foo: function
        :return: 构造的节点对象
        """
        # 存储返回引脚的 AnyPin 选项
        retAnyOpts = None
        # 存储返回引脚的约束条件
        retConstraint = None
        # 获取函数的元数据
        meta = foo.__annotations__["meta"]
        # 存储返回值的类型
        returnType = returnDefaultValue = None
        # 存储返回引脚要启用的选项
        returnPinOptionsToEnable = None
        # 存储返回引脚要禁用的选项
        returnPinOptionsToDisable = None
        # 存储返回引脚的输入部件变体
        returnWidgetVariant = "DefaultWidget"
        # 存储返回引脚的结构约束条件
        retStructConstraint = None
        # 存储返回值的注释字典
        returnAnnotationDict = None
        if foo.__annotations__["return"] is not None:
            # 获取返回值的类型
            returnType = foo.__annotations__["return"][0]
            # 获取返回值的默认值
            returnDefaultValue = foo.__annotations__["return"][1]
            if len(foo.__annotations__["return"]) == 3:
                # 获取返回值的注释字典
                returnAnnotationDict = foo.__annotations__["return"][2]

                if PinSpecifiers.SUPPORTED_DATA_TYPES in returnAnnotationDict:
                    # 获取返回引脚的 AnyPin 选项
                    retAnyOpts = returnAnnotationDict[
                        PinSpecifiers.SUPPORTED_DATA_TYPES
                    ]
                if PinSpecifiers.CONSTRAINT in returnAnnotationDict:
                    # 获取返回引脚的约束条件
                    retConstraint = returnAnnotationDict[PinSpecifiers.CONSTRAINT]
                if PinSpecifiers.STRUCT_CONSTRAINT in returnAnnotationDict:
                    # 获取返回引脚的结构约束条件
                    retStructConstraint = returnAnnotationDict[
                        PinSpecifiers.STRUCT_CONSTRAINT
                    ]
                if PinSpecifiers.ENABLED_OPTIONS in returnAnnotationDict:
                    # 获取返回引脚要启用的选项
                    returnPinOptionsToEnable = returnAnnotationDict[
                        PinSpecifiers.ENABLED_OPTIONS
                    ]
                if PinSpecifiers.DISABLED_OPTIONS in returnAnnotationDict:
                    # 获取返回引脚要禁用的选项
                    returnPinOptionsToDisable = returnAnnotationDict[
                        PinSpecifiers.DISABLED_OPTIONS
                    ]
                if PinSpecifiers.INPUT_WIDGET_VARIANT in returnAnnotationDict:
                    # 获取返回引脚的输入部件变体
                    returnWidgetVariant = returnAnnotationDict[
                        PinSpecifiers.INPUT_WIDGET_VARIANT
                    ]

        # 获取函数的节点类型
        nodeType = foo.__annotations__["nodeType"]
        # 获取函数所属的包名
        _packageName = foo.__annotations__["packageName"]
        # 获取函数所属的库名
        libName = foo.__annotations__["lib"]
        # 获取函数的参数名列表
        fooArgNames = getfullargspec(foo).args

        @staticmethod
        def description():
            """
            返回函数的文档字符串作为节点的描述信息。

            :return: 函数的文档字符串
            """
            return foo.__doc__

        @staticmethod
        def category():
            """
            返回函数元数据中的类别作为节点的类别。

            :return: 节点的类别
            """
            return meta[NodeMeta.CATEGORY]

        @staticmethod
        def keywords():
            """
            返回函数元数据中的关键字列表作为节点的关键字列表。

            :return: 节点的关键字列表
            """
            return meta[NodeMeta.KEYWORDS]

        def constructor(self, name, **kwargs):
            """
            节点类的构造函数，调用 NodeBase 的构造函数。

            :param name: 节点的名称
            :param kwargs: 关键字参数
            """
            NodeBase.__init__(self, name, **kwargs)

        # 动态创建节点类
        nodeClass = type(
            foo.__name__,
            (NodeBase,),
            {
                "__init__": constructor,
                "category": category,
                "keywords": keywords,
                "description": description,
            },
        )

        # 设置节点类所属的包名
        nodeClass._packageName = _packageName

        # 创建节点类的实例
        raw_inst = nodeClass(foo.__name__)
        # 设置节点实例所属的库名
        raw_inst.lib = libName

        # 存储引用输出引脚的列表
        refs = []
        # 存储输出执行引脚
        outExec = None

        # 生成节点的 compute 方法
        def compute(self, *args, **kwargs):
            """
            节点的计算方法，从输入引脚获取数据，调用函数并设置返回值到输出引脚。

            :param args: 可变参数
            :param kwargs: 关键字参数
            """
            # 从输入引脚获取数据
            kwds = {}
            for i in list(self.inputs.values()):
                if not i.isExec():
                    kwds[i.name] = i.getData()
            for ref in refs:
                if not ref.isExec():
                    kwds[ref.name] = ref.setData
            # 设置函数的所属节点
            foo.owningNode = self
            # 调用函数
            result = foo(**kwds)
            if returnType is not None:
                # 设置返回值到输出引脚
                self.setData(str("out"), result)
            if nodeType == NodeTypes.Callable:
                # 调用输出执行引脚
                outExec.call(*args, **kwargs)

        # 将 compute 方法绑定到节点实例
        raw_inst.compute = MethodType(compute, raw_inst)

        # 设置节点实例的元数据
        raw_inst._nodeMetaData = meta
        if "CacheEnabled" in meta:
            # 根据元数据设置节点是否启用缓存
            raw_inst.bCacheEnabled = meta["CacheEnabled"]

        # 如果节点类型为可调用，创建输入和输出执行引脚
        if nodeType == NodeTypes.Callable:
            raw_inst.createInputPin(
                DEFAULT_IN_EXEC_NAME, "ExecPin", None, raw_inst.compute
            )
            outExec = raw_inst.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin")
            raw_inst.bCallable = True
            raw_inst.bCacheEnabled = False

        if returnType is not None:
            # 创建返回值输出引脚
            p = raw_inst.createOutputPin(
                "out",
                returnType,
                returnDefaultValue,
                supportedPinDataTypes=retAnyOpts,
                constraint=retConstraint,
                structConstraint=retStructConstraint,
            )
            # 设置引脚的数据和默认值
            p.setData(returnDefaultValue)
            p.setDefaultValue(returnDefaultValue)
            # 初始化引脚的数组或字典类型
            p.initAsArray(isinstance(returnDefaultValue, list))
            p.initAsDict(isinstance(returnDefaultValue, dict))
            # 设置引脚的输入部件变体
            p.setInputWidgetVariant(returnWidgetVariant)
            # 设置引脚的注释描述字典
            p.annotationDescriptionDict = (
                copy(returnAnnotationDict) if returnAnnotationDict is not None else None
            )
            if (
                p.annotationDescriptionDict is not None
                and "Description" in p.annotationDescriptionDict
            ):
                # 设置引脚的描述信息
                p.description = p.annotationDescriptionDict["Description"]
            if returnPinOptionsToEnable is not None:
                # 启用引脚的选项
                p.enableOptions(returnPinOptionsToEnable)
            if returnPinOptionsToDisable is not None:
                # 禁用引脚的选项
                p.disableOptions(returnPinOptionsToDisable)
            if not p.isArray() and p.optionEnabled(PinOptions.ArraySupported):
                # 设置引脚的结构类型为 Multi
                p.structureType = StructureType.Multi
            elif p.isArray():
                # 设置引脚的结构类型为 Array
                p.structureType = StructureType.Array

        # 遍历函数的参数，根据参数注释创建引脚
        for index in range(len(fooArgNames)):
            argName = fooArgNames[index]
            # 获取参数的注释元组
            pinDescriptionTuple = foo.__annotations__[argName]
            # 存储引脚的 AnyPin 选项
            anyOpts = None
            # 存储引脚的约束条件
            constraint = None
            # 存储引脚的结构约束条件
            structConstraint = None
            # 存储引脚要启用的选项
            pinOptionsToEnable = None
            # 存储引脚要禁用的选项
            pinOptionsToDisable = None
            # 存储引脚的输入部件变体
            inputWidgetVariant = "DefaultWidget"
            # 元组表示这是一个引用引脚，带有默认值，例如 - (dataType, defaultValue)
            if str("Reference") == pinDescriptionTuple[0]:
                # 获取引用引脚的数据类型
                pinDataType = pinDescriptionTuple[1][0]
                # 获取引用引脚的默认值
                pinDefaultValue = pinDescriptionTuple[1][1]
                # 存储引用引脚的注释字典
                pinDict = None
                if len(pinDescriptionTuple[1]) == 3:
                    pinDict = pinDescriptionTuple[1][2]

                if pinDict is not None:
                    if PinSpecifiers.SUPPORTED_DATA_TYPES in pinDict:
                        # 获取引用引脚的 AnyPin 选项
                        anyOpts = pinDict[PinSpecifiers.SUPPORTED_DATA_TYPES]
                    if PinSpecifiers.CONSTRAINT in pinDict:
                        # 获取引用引脚的约束条件
                        constraint = pinDict[PinSpecifiers.CONSTRAINT]
                    if PinSpecifiers.STRUCT_CONSTRAINT in pinDict:
                        # 获取引用引脚的结构约束条件
                        structConstraint = pinDict[PinSpecifiers.STRUCT_CONSTRAINT]
                    if PinSpecifiers.ENABLED_OPTIONS in pinDict:
                        # 获取引用引脚要启用的选项
                        pinOptionsToEnable = pinDict[PinSpecifiers.ENABLED_OPTIONS]
                    if PinSpecifiers.DISABLED_OPTIONS in pinDict:
                        # 获取引用引脚要禁用的选项
                        pinOptionsToDisable = pinDict[PinSpecifiers.DISABLED_OPTIONS]
                    if PinSpecifiers.INPUT_WIDGET_VARIANT in pinDict:
                        # 获取引用引脚的输入部件变体
                        inputWidgetVariant = pinDict[PinSpecifiers.INPUT_WIDGET_VARIANT]

                # 创建引用输出引脚
                outRef = raw_inst.createOutputPin(
                    argName,
                    pinDataType,
                    supportedPinDataTypes=anyOpts,
                    constraint=constraint,
                    structConstraint=structConstraint,
                )
                # 设置引用输出引脚的注释描述字典
                outRef.annotationDescriptionDict = (
                    copy(pinDict) if pinDict is not None else None
                )
                if (
                    outRef.annotationDescriptionDict is not None
                    and "Description" in outRef.annotationDescriptionDict
                ):
                    # 设置引用输出引脚的描述信息
                    outRef.description = outRef.annotationDescriptionDict["Description"]
                # 初始化引用输出引脚的数组或字典类型
                outRef.initAsArray(isinstance(pinDefaultValue, list))
                outRef.initAsDict(isinstance(pinDefaultValue, dict))
                # 设置引用输出引脚的默认值和数据
                outRef.setDefaultValue(pinDefaultValue)
                outRef.setData(pinDefaultValue)
                # 设置引用输出引脚的输入部件变体
                outRef.setInputWidgetVariant(inputWidgetVariant)
                if pinOptionsToEnable is not None:
                    # 启用引用输出引脚的选项
                    outRef.enableOptions(pinOptionsToEnable)
                if pinOptionsToDisable is not None:
                    # 禁用引用输出引脚的选项
                    outRef.disableOptions(pinOptionsToDisable)
                if not outRef.isArray() and outRef.optionEnabled(
                    PinOptions.ArraySupported
                ):
                    # 设置引用输出引脚的结构类型为 Multi
                    outRef.structureType = StructureType.Multi
                elif outRef.isArray():
                    # 设置引用输出引脚的结构类型为 Array
                    outRef.structureType = StructureType.Array
                # 将引用输出引脚添加到列表中
                refs.append(outRef)
            else:
                # 获取输入引脚的数据类型
                pinDataType = pinDescriptionTuple[0]
                # 获取输入引脚的默认值
                pinDefaultValue = pinDescriptionTuple[1]
                # 存储输入引脚的注释字典
                pinDict = None
                if len(pinDescriptionTuple) == 3:
                    pinDict = pinDescriptionTuple[2]

                if pinDict is not None:
                    if PinSpecifiers.SUPPORTED_DATA_TYPES in pinDict:
                        # 获取输入引脚的 AnyPin 选项
                        anyOpts = pinDict[PinSpecifiers.SUPPORTED_DATA_TYPES]
                    if PinSpecifiers.CONSTRAINT in pinDict:
                        # 获取输入引脚的约束条件
                        constraint = pinDict[PinSpecifiers.CONSTRAINT]
                    if PinSpecifiers.STRUCT_CONSTRAINT in pinDict:
                        # 获取输入引脚的结构约束条件
                        structConstraint = pinDict[PinSpecifiers.STRUCT_CONSTRAINT]
                    if PinSpecifiers.ENABLED_OPTIONS in pinDict:
                        # 获取输入引脚要启用的选项
                        pinOptionsToEnable = pinDict[PinSpecifiers.ENABLED_OPTIONS]
                    if PinSpecifiers.DISABLED_OPTIONS in pinDict:
                        # 获取输入引脚要禁用的选项
                        pinOptionsToDisable = pinDict[PinSpecifiers.DISABLED_OPTIONS]
                    if PinSpecifiers.INPUT_WIDGET_VARIANT in pinDict:
                        # 获取输入引脚的输入部件变体
                        inputWidgetVariant = pinDict[PinSpecifiers.INPUT_WIDGET_VARIANT]

                # 创建输入引脚
                inp = raw_inst.createInputPin(
                    argName,
                    pinDataType,
                    supportedPinDataTypes=anyOpts,
                    constraint=constraint,
                    structConstraint=structConstraint,
                )
                # 设置输入引脚的注释描述字典
                inp.annotationDescriptionDict = (
                    copy(pinDict) if pinDict is not None else None
                )
                if (
                    inp.annotationDescriptionDict is not None
                    and "Description" in inp.annotationDescriptionDict
                ):
                    # 设置输入引脚的描述信息
                    inp.description = inp.annotationDescriptionDict["Description"]
                # 初始化输入引脚的数组或字典类型
                inp.initAsArray(isinstance(pinDefaultValue, list))
                inp.initAsDict(isinstance(pinDefaultValue, dict))
                # 设置输入引脚的默认值和数据
                inp.setData(pinDefaultValue)
                inp.setDefaultValue(pinDefaultValue)
                # 设置输入引脚的输入部件变体
                inp.setInputWidgetVariant(inputWidgetVariant)
                if pinOptionsToEnable is not None:
                    # 启用输入引脚的选项
                    inp.enableOptions(pinOptionsToEnable)
                if pinOptionsToDisable is not None:
                    # 禁用输入引脚的选项
                    inp.disableOptions(pinOptionsToDisable)
                if not inp.isArray() and inp.optionEnabled(PinOptions.ArraySupported):
                    # 设置输入引脚的结构类型为 Multi
                    inp.structureType = StructureType.Multi
                elif inp.isArray():
                    # 设置输入引脚的结构类型为 Array
                    inp.structureType = StructureType.Array
        # 自动设置引脚之间的影响关系
        raw_inst.autoAffectPins()
        return raw_inst